package com.yami.shop.discount.common.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.enums.OfflineHandleEventStatus;
import com.yami.shop.bean.enums.OfflineHandleEventType;
import com.yami.shop.bean.model.OfflineHandleEvent;
import com.yami.shop.bean.model.ProdLang;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.constants.DiscountStatusEnum;
import com.yami.shop.discount.common.dao.DiscountItemMapper;
import com.yami.shop.discount.common.dao.DiscountMapper;
import com.yami.shop.discount.common.dao.DiscountProdMapper;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.manager.impl.LangManager;
import com.yami.shop.service.OfflineHandleEventService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lgh on 2018/11/21.
 */
@Service
@AllArgsConstructor
public class DiscountServiceImpl extends ServiceImpl<DiscountMapper, Discount> implements DiscountService {

    private final DiscountMapper discountMapper;
    private final DiscountItemMapper discountItemMapper;
    private final DiscountProdMapper discountProdMapper;
    private final OfflineHandleEventService offlineHandleEventService;
    private final LangManager langManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(cacheNames = "DiscountAndItemAndProd", key = "#discount.discountId"),
            @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#discount.shopId")
    })
    public void updateDiscountAndItemAndProd(@Valid Discount discount) {
        Discount selectById = discountMapper.selectById(discount.getDiscountId());
        if (selectById.getStatus() > DiscountStatusEnum.RUN.getValue() && discount.getStatus() < DiscountStatusEnum.OFFLINE.getValue()){
            // 该活动已被平台下线，不能再更改状态
            throw new YamiShopBindException("yami.activity.status.check");
        }
        if (!Objects.equals(selectById.getShopId(), discount.getShopId())) {
            throw new YamiShopBindException("当前活动信息不属于您的店铺");
        }
        discountMapper.updateById(discount);

        discountItemMapper.delete(new LambdaQueryWrapper<DiscountItem>().eq(DiscountItem::getDiscountId, discount.getDiscountId()));

        List<DiscountItem> discountItems = discount.getDiscountItems();
        insertDiscountItems(discount, discountItems);

        discountProdMapper.delete(new LambdaQueryWrapper<DiscountProd>().eq(DiscountProd::getDiscountId, discount.getDiscountId()));

        if (discount.getSuitableProdType() != 0) {
            List<DiscountProd> discountProds = discount.getDiscountProds();
            for (DiscountProd discountProd : discountProds) {
                discountProd.setDiscountId(discount.getDiscountId());
            }
            if (CollectionUtil.isNotEmpty(discountProds)) {
                discountProdMapper.insertDiscountProds(discountProds);
            }
        }
    }

    /**
     * 插入满减项
     * @param discount
     * @param discountItems
     */
    private void insertDiscountItems(@Valid Discount discount, List<DiscountItem> discountItems) {
        for (DiscountItem discountItem : discountItems) {
            if (discountItem.getNeedAmount() == null || discountItem.getDiscount() == null) {
                // 请填写完整满减信息
                throw new YamiShopBindException("yami.activity.info.check");
            }
            discountItem.setDiscountId(discount.getDiscountId());
        }
        discountItemMapper.insertDiscountItems(discountItems);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#discount.shopId")
    public void insertDiscountAndItemAndProd(@Valid Discount discount) {
        discountMapper.insert(discount);

        List<DiscountItem> discountItems = discount.getDiscountItems();
        if (CollectionUtil.isEmpty(discountItems)) {
            // 活动项不能为空，最少要有一个活动项
            throw new YamiShopBindException("yami.activity.item.check");
        }
        insertDiscountItems(discount, discountItems);
        if (discount.getSuitableProdType() != 0) {
            List<DiscountProd> discountProds = discount.getDiscountProds();
            if (CollectionUtil.isNotEmpty(discountProds)) {
                for (DiscountProd discountProd : discountProds) {
                    discountProd.setDiscountId(discount.getDiscountId());
                }
                discountProdMapper.insertDiscountProds(discountProds);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(cacheNames = "DiscountAndItemAndProd", key = "#discountId"),
            @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#shopId")
    })
    public void deleteDiscountsAndItemsAndProds(Long discountId, Long shopId) {
        int i = discountMapper.deleteDiscounts(discountId, shopId);
        if (i > 0) {
            discountItemMapper.delete(new LambdaQueryWrapper<DiscountItem>().eq(DiscountItem::getDiscountId, discountId));
            discountProdMapper.delete(new LambdaQueryWrapper<DiscountProd>().eq(DiscountProd::getDiscountId, discountId));
        }
    }

    @Override
    public Discount getDiscountAndItemAndProdById(Long discountId) {
        DiscountServiceImpl discountService = (DiscountServiceImpl) AopContext.currentProxy();
        Discount discount = discountService.getDiscounttAndItemAndProdByDiscountId(discountId);
        List<Long> prodIds = discount.getDiscountProds().stream().filter(discountProd -> Objects.nonNull(discountProd.getProdId())).map(DiscountProd::getProdId).collect(Collectors.toList());
        Map<Long, ProdLang> prodLangMap = langManager.getProdLangMap(prodIds);
        for (DiscountProd discountProd : discount.getDiscountProds()) {
            ProdLang prodLang = prodLangMap.get(discountProd.getProdId());
            if (Objects.nonNull(prodLang)) {
                discountProd.setProdName(prodLang.getProdName());
            }
        }
        return discount;
    }

    @Override
    @Cacheable(cacheNames = "DiscountAndItemAndProd", key = "#discountId")
    public Discount getDiscounttAndItemAndProdByDiscountId(Long discountId) {
        return discountMapper.getDiscountAndItemAndProdById(discountId);
    }

    @Override
    @CacheEvict(cacheNames = "DiscountAndItemAndProd", key = "#discountId")
    public void removeDiscountAndItemAndProdCacheById(Long discountId) {
    }

    @Override
    @Cacheable(cacheNames = "DiscountAndItemAndProdByShopId", key = "#shopId")
    public List<Discount> listDiscountsAndItemsByShopId(Long shopId) {
        return discountMapper.getDiscountsAndItemsByShopId(shopId);
    }

    @Override
    @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#shopId")
    public void removeDiscountsAndItemsCacheByShopId(Long shopId) {
    }

    @Override
    public List<DiscountDto> listByProdId(Long prodId, Long shopId) {
        return discountMapper.listByProdId(prodId,shopId);
    }


    @Override
    public IPage<ProductDto> discountProdList(PageParam<ProductDto> page, Long discountId) {
        Discount discount = getOne(new LambdaQueryWrapper<Discount>().eq(Discount::getDiscountId, discountId)
                .eq(Discount::getStatus, 1).le(Discount::getStartTime, new Date()).gt(Discount::getEndTime, new Date()));
        Map<Long, Long> prodMap = new LinkedHashMap<>();
        if(Objects.isNull(discount)){
            discount = new Discount();
            discount.setDiscountId(0L);
        }else{
            List<DiscountProd> discountProds = discountProdMapper.getDiscountProdByDiscountId(discountId);
            prodMap = discountProds.stream().collect(Collectors.toMap(DiscountProd::getDiscountProdId, DiscountProd::getProdId));
        }
        IPage<ProductDto> iPage = discountMapper.discountProdList(page, discount);
        langManager.getProdDtoLang(iPage.getRecords());
        if(discount.getSuitableProdType() == null || discount.getSuitableProdType() != 1){
            return iPage;
        }
        if(!prodMap.isEmpty()){
            List<ProductDto> prodList = new ArrayList<>();
            Map<Long, ProductDto> prodMaps = iPage.getRecords().stream().collect(Collectors.toMap(ProductDto::getProdId, s->s));
            for(Map.Entry<Long, Long> entry : prodMap.entrySet()) {
                if(prodMaps.containsKey(entry.getValue())) {
                    prodList.add(prodMaps.get(entry.getValue()));
                }
            }
            iPage.setRecords(prodList);
        }
        return iPage;
    }

    @Override
    public IPage<Discount> getPlatformDiscountPage(PageParam<Discount> page, Discount discount) {
        return discountMapper.getPlatformDiscountPage(page, discount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void offline(Discount discount, String offlineReason, Long sysUserId) {
        Date now = new Date();
        OfflineHandleEvent offlineHandleEvent = new OfflineHandleEvent();
        offlineHandleEvent.setHandleId(discount.getDiscountId());
        offlineHandleEvent.setHandleType(OfflineHandleEventType.DISCOUNT.getValue());
        offlineHandleEvent.setHandlerId(sysUserId);
        offlineHandleEvent.setCreateTime(now);
        offlineHandleEvent.setOfflineReason(offlineReason);
        offlineHandleEvent.setShopId(discount.getShopId());
        offlineHandleEvent.setStatus(OfflineHandleEventStatus.OFFLINE_BY_PLATFORM.getValue());
        offlineHandleEvent.setUpdateTime(now);
        offlineHandleEventService.save(offlineHandleEvent);
        // 更新活动状态为下线
        discountMapper.updateStatusByDiscountId(discount.getDiscountId(), DiscountStatusEnum.OFFLINE.getValue());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditApply(Long eventId, Long discountId, String reapplyReason) {
        // 更新活动为待审核状态
        discountMapper.updateStatusByDiscountId(discountId, DiscountStatusEnum.PLATFORM_AUDIT.getValue());
        // 更新事件为待审核
        offlineHandleEventService.updateToApply(eventId, reapplyReason);
    }


    @Override
    public void auditDiscount(OfflineHandleEventAuditParam offlineHandleEventAuditParam, Long sysUserId) {
        // 审核通过
        if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.AGREE_BY_PLATFORM.getValue())) {
            // 更新商品状态
            discountMapper.updateStatusByDiscountId(offlineHandleEventAuditParam.getHandleId(), DiscountStatusEnum.CLOSE.getValue());
        }
        // 审核不通过
        else if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.DISAGREE_BY_PLATFORM.getValue())) {
            discountMapper.updateStatusByDiscountId(offlineHandleEventAuditParam.getHandleId(), DiscountStatusEnum.OFFLINE.getValue());
        }
        offlineHandleEventService.auditOfflineEvent(offlineHandleEventAuditParam, sysUserId);
    }

    @Override
    public IPage<DiscountDto> getDiscountList(PageParam<Discount> page) {
        return discountMapper.getDiscountList(page);
    }

    @Override
    public DiscountDto getDiscountByDiscountId(Long discountId) {
        return discountMapper.getDiscountByDiscountId(discountId);
    }

    @Override
    public void closeDiscountBySetStatus() {
        discountMapper.closeDiscountBySetStatus();
    }

    @Override
    public Integer countProdIsSeckillAll(Long shopId, Long prodId) {
        return discountMapper.countProdIsSeckillAll(shopId,prodId);
    }

    @Override
    public Integer countProdIsSeckillSome(Long shopId, Long prodId) {
        return discountMapper.countProdIsSeckillSome(shopId,prodId);
    }

    @Override
    public Integer countProdIsSeckillExcept(Long shopId, Long prodId) {
        return discountMapper.countProdIsSeckillExcept(shopId,prodId);
    }

    @Override
    public List<Long> getProdIsSeckillExcept(Long shopId) {
        List<DiscountProd> list = discountMapper.getProdIsSeckillExcept(shopId);
        List<Long> prodIds = list.stream().map(t -> t.getProdId()).distinct().collect(Collectors.toList());
        return prodIds;
    }

    @Override
    public IPage<Product> pageProdByDiscount(PageParam<Product> page, ProductParam product, Discount discount) {
        IPage<Product> iPage = discountMapper.pageProdByDiscount(page, product, discount);
        langManager.getProdLang(iPage.getRecords());
        if(discount.getSuitableProdType() != 1){
            return iPage;
        }
        List<DiscountProd> discountProds = discountProdMapper.getDiscountProdByDiscountId(discount.getDiscountId());
        Map<Long, Long> prodMap = discountProds.stream().collect(Collectors.toMap(DiscountProd::getDiscountProdId, DiscountProd::getProdId));
        List<Product> prodList = new ArrayList<>();
        Map<Long, Product> prodMaps = iPage.getRecords().stream().collect(Collectors.toMap(Product::getProdId, s->s));
        for(Map.Entry<Long, Long> entry : prodMap.entrySet()){
            if(prodMaps.containsKey(entry.getValue())){
                prodList.add(prodMaps.get(entry.getValue()));
            }
        }
        iPage.setRecords(prodList);
        return iPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> updateDiscountProdByIds(List<Long> prodIds) {
        // 查询出所有为可用商品类型的满减活动的，包含需要处理商品id，进行删除
        List<DiscountProd> discountProd = discountItemMapper.listDiscountByProdIds(prodIds);
        List<Long> prodIdsDb = new ArrayList<>();
        List<Long> ids = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(discountProd)){
            for (DiscountProd prod : discountProd) {
                ids.add(prod.getDiscountId());
                prodIdsDb.add(prod.getProdId());
            }
            discountProdMapper.delete(new LambdaQueryWrapper<DiscountProd>().in(DiscountProd::getProdId,prodIdsDb));
        }
        return ids;
    }

    @Override
    public List<Long> listShopIdsWhichWillClose() {
        return discountMapper.listShopIdsWhichWillClose();
    }

    @Override
    public List<Long> listIdByShopIdAndStatus(Long shopId, Integer status) {
        return discountMapper.listIdByShopIdAndStatus(shopId, status);
    }


}
